Data Read-in

https://docs.google.com/spreadsheets/d/1nDXokcwtq_J8A6Uh7zAxsWW16MtcEC_1EiLstz4WGig/edit#gid=1782979571

Social_2022_rawNA_data <- readr::read_csv("DATA/Social Query 2022 NA Campaigns.csv") %>%
  clean_names() %>% 
  filter(date < '2022-07-01') %>% 
  filter(impressions_analyzed > 10)
Rows: 24117 Columns: 149
-- Column specification ---------------------------------------------------------------------
Delimiter: ","
chr   (4): olive_plan_name, platform, olive_placement_type, last_file_date
dbl  (99): olive_plan_id, olive_placement_id, brand_id, opid, _1_sec_in_view_impressions,...
lgl  (44): active_view_eligible_impressions, active_view_measurable_impressions, active_v...
date  (2): date, ingestion_date

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
Social_2022_rawNA_lookup <- readr::read_csv("DATA/CampaignLookup.csv") %>%
  clean_names()
Rows: 9 Columns: 4
-- Column specification ---------------------------------------------------------------------
Delimiter: ","
chr (3): Name, Spend, Quarter
dbl (1): OPID

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
Social_2022_MOAT_lookup = googlesheets4::read_sheet(
  "https://docs.google.com/spreadsheets/d/1nDXokcwtq_J8A6Uh7zAxsWW16MtcEC_1EiLstz4WGig/edit#gid=1782979571") %>% 
  clean_names() %>% 
  filter(social == 'Y')
Auto-refreshing stale OAuth token.
√ Reading from Benchmark_Moat_Tile_2022.
√ Range Benchmark_Moat_Tile_2022.

Summary Stats

Placement types distinct


Social_2022_rawNA_data %>%
  group_by(platform,olive_placement_type) %>% 
  summarize (counts = n())
`summarise()` has grouped output by 'platform'. You can override using the `.groups` argument.
Social_2022_rawNA_data %>%
  group_by(platform,brand_id) %>% 
  summarize (counts = n()) %>%
  left_join(Social_2022_MOAT_lookup, by = 'brand_id')
`summarise()` has grouped output by 'platform'. You can override using the `.groups` argument.

Social_2022_processedNA_data <-
  Social_2022_rawNA_data %>%
  select(
    olive_plan_id:opid, impressions_analyzed,
    x2_sec_video_in_view_impressions, in_view_impressions, fully_on_screen_3sec_cumulative, player_vis_and_aud_on_complete_sum,
    valid_and_avoc, valid_and_viewable, valid_and_viewable_gm, valid_and_fully_on_screen_3sec_cumulative, valid_and_inview_3sec_cumulative
  ) %>%
  rename(platform_old = platform
         ) %>% 
  left_join(Social_2022_MOAT_lookup, by = 'brand_id'
  ) %>% 
  mutate(
    olive_plan_id = as.character(olive_plan_id),
    olive_placement_id = as.character(olive_placement_id),
    brand_id = as.character(brand_id) ,
    opid = as.character(opid)
  )

#Look at global moat

Social_2022_processedNA_data %>% 
  skim()
-- Data Summary ------------------------
                           Values    
Name                       Piped data
Number of rows             16137     
Number of columns          24        
_______________________              
Column type frequency:               
  character                13        
  Date                     1         
  numeric                  10        
________________________             
Group variables            None      

-- Variable type: character ---------------------------------------------------------------------------------------
# A tibble: 13 x 8
   skim_variable        n_missing complete_rate   min   max empty n_unique whitespace
 * <chr>                    <int>         <dbl> <int> <int> <int>    <int>      <int>
 1 olive_plan_id                0             1     5     5     0        8          0
 2 olive_plan_name              0             1    44    72     0        8          0
 3 platform_old                 0             1     6    18     0        7          0
 4 olive_placement_id           0             1     7     7     0      364          0
 5 olive_placement_type         0             1    15    27     0        3          0
 6 brand_id                     0             1     5     7     0       14          0
 7 opid                         0             1     7     7     0      364          0
 8 dataset_name                 0             1    20    51     0       14          0
 9 region                       0             1     2     6     0        2          0
10 channel                      0             1     5     7     0        2          0
11 social                       0             1     1     1     0        1          0
12 platform                     0             1     6     9     0        6          0
13 media_type                   0             1     5    15     0        4          0

-- Variable type: Date --------------------------------------------------------------------------------------------
# A tibble: 1 x 7
  skim_variable n_missing complete_rate min        max        median     n_unique
* <chr>             <int>         <dbl> <date>     <date>     <date>        <int>
1 date                  0             1 2022-01-18 2022-06-30 2022-03-03      164

-- Variable type: numeric -----------------------------------------------------------------------------------------
# A tibble: 10 x 11
   skim_variable                             n_missing complete_rate    mean      sd    p0   p25    p50     p75
 * <chr>                                         <int>         <dbl>   <dbl>   <dbl> <dbl> <dbl>  <dbl>   <dbl>
 1 impressions_analyzed                              0         1     209721. 518214.    11 2222  27191  166005 
 2 x2_sec_video_in_view_impressions               6641         0.588  56682. 191926.     0  255.  4414   26352.
 3 in_view_impressions                           10589         0.344 106745. 228837.     1  311. 39378  121618.
 4 fully_on_screen_3sec_cumulative                9578         0.406  39246. 135769.     0   41   1339   17771 
 5 player_vis_and_aud_on_complete_sum             8659         0.463    864.   2336.     0    0     43     452 
 6 valid_and_avoc                                 8659         0.463    862.   2322.     0    0     43     452 
 7 valid_and_viewable                              257         0.984  71189. 202864.     0  124   7672.  55972.
 8 valid_and_viewable_gm                         11095         0.312 190179. 444404.     0  149  41944  117986.
 9 valid_and_fully_on_screen_3sec_cumulative      9578         0.406  39245. 135767.     0   41   1339   17771 
10 valid_and_inview_3sec_cumulative               6221         0.614  19872.  83958.     0   57   1502   13010 
      p100 hist 
 *   <dbl> <chr>
 1 9704935 ▇▁▁▁▁
 2 6361782 ▇▁▁▁▁
 3 6120199 ▇▁▁▁▁
 4 5942621 ▇▁▁▁▁
 5   34734 ▇▁▁▁▁
 6   34040 ▇▁▁▁▁
 7 6361782 ▇▁▁▁▁
 8 7095033 ▇▁▁▁▁
 9 5942621 ▇▁▁▁▁
10 5942621 ▇▁▁▁▁

Plot Impression Level Data


p = Social_2022_processedNA_data %>% 
  ggplot() +
    aes(x = date, y = impressions_analyzed, color = olive_plan_name, fill = olive_plan_name) +
  xlab("Date") +
  ylab("Impressions") +
  ggtitle("Impressions by Campaign") + #fix size
geom_bar(stat = 'identity') +
  theme_bw() +
  theme(
     plot.title = element_text(size=22, hjust = 0.5),
     axis.title.y = element_blank()
  )

p


ggplotly(p)

Calculate V/V and AVOC Rates by Platform

Social_2022_SummarizedNA_date <-
Social_2022_processedNA_data %>%
  mutate(
    valid_viewable_imps =
      case_when(
        platform == 'Twitter' & channel == 'Display' ~ valid_and_inview_3sec_cumulative,
        platform == 'LinkedIn' ~ x2_sec_video_in_view_impressions,
        TRUE ~ valid_and_viewable
      ),
    avoc_imps = case_when(
    TRUE ~  player_vis_and_aud_on_complete_sum 
    ),
    quarter = lubridate::quarter(date),
    channel = case_when(
      channel == 'DISPLAY' ~ 'STATIC',
      TRUE ~ channel
    )
  ) %>% 
  group_by(olive_plan_name,date, channel, platform) %>% 
  summarize(
    valid_viewable_imps = sum(valid_viewable_imps),
    avoc_imps = sum(avoc_imps),
    impressions = sum(impressions_analyzed),
    quarter = max(quarter)
  ) %>% 
  arrange(date,olive_plan_name)
`summarise()` has grouped output by 'olive_plan_name', 'date', 'channel'. You can override using the `.groups` argument.

Social_2022_SummarizedNA_date %>% 
  filter(platform == 'Twitter') %>% 
  filter(channel == 'DISPLAY')
NA

Social_2022_SummarizedNA_date %>% 
  group_by(quarter, platform, channel) %>% 
  summarize(
    `Valid and Viewable Rate` = sum(valid_viewable_imps)/sum(impressions),
    `AVOC Rate` = sum(avoc_imps)/sum(impressions)
  ) %>%     
  kbl() %>% 
 kable_material(c("striped", "hover","condensed","responsive"),full_width = F,fixed_thead = T)
`summarise()` has grouped output by 'quarter', 'platform'. You can override using the `.groups` argument.
quarter platform channel Valid and Viewable Rate AVOC Rate
1 Facebook STATIC 0.54501 NA
1 Facebook VIDEO 0.25649 0.01066
1 Instagram STATIC 0.29165 NA
1 Instagram VIDEO 0.12315 0.00806
1 Reddit STATIC 0.60063 NA
1 Reddit VIDEO 0.08513 0.00116
1 Snapchat STATIC 1.00000 NA
1 Snapchat VIDEO 0.29612 NA
1 Twitter STATIC NA NA
1 Twitter VIDEO 0.52051 0.00116
2 Facebook STATIC NA NA
2 Facebook VIDEO 0.15835 0.00505
2 Instagram STATIC NA NA
2 Instagram VIDEO 0.10855 0.00765
2 Reddit STATIC 0.63588 NA
2 Reddit VIDEO 0.10184 0.00069
2 Snapchat STATIC 1.00000 NA
2 Snapchat VIDEO 0.25486 NA
2 TikTok VIDEO 0.09412 0.00426
2 Twitter STATIC NA NA
2 Twitter VIDEO 0.55424 0.00084
write_sheet(Social_2022_SummarizedNA_date, ss = 'https://docs.google.com/spreadsheets/d/1OT7zoqbcnadQsR8ehHbvMfKn8mieWYa-dVaCOHFdhHI/edit#gid=1469978055',
           sheet = 'Aggregated Data NA')
Auto-refreshing stale OAuth token.
√ Writing to Social Query 2022 Results Updated.
√ Writing to sheet Aggregated Data NA.

Additional Twitter Level Information

Social_2022_processedTwitterNA_data <-
Social_2022_processedNA_data %>% select(olive_plan_name,olive_placement_id,date,impressions_analyzed) %>%
  mutate(
    quarter = quarter(date)
  ) %>% 
  group_by(olive_plan_name,olive_placement_id,date,quarter) %>% 
  summarize(
    impressions_analyzed = sum(impressions_analyzed)
  ) %>%
  ungroup() %>% 
  left_join(Social_2022_rawNA_DV30_data, by = c('olive_placement_id','olive_plan_name', 'date')) %>% 
  filter(!is.na(olive_plan_name)) %>% 
  filter(platform == 'Twitter - Official' & olive_placement_type == 'Standard Banner (BAN)') %>% 
  mutate(
    impressions = impressions_analyzed,
    platform = 'Twitter',
    channel = 'STATIC',
    avoc_imps = NA,
    valid_viewable_imps = valid_and_viewable_impressions,
    Time = NA
  ) %>% 
  select(olive_plan_name,   date,   channel,    platform,   valid_viewable_imps,    avoc_imps,  impressions,    Time,   quarter)
`summarise()` has grouped output by 'olive_plan_name', 'olive_placement_id', 'date'. You can override using the `.groups` argument.

Update Twitter Information



write_sheet(Social_2022_SummarizedNA_data_updatedTwitter, ss = 'https://docs.google.com/spreadsheets/d/1OT7zoqbcnadQsR8ehHbvMfKn8mieWYa-dVaCOHFdhHI/edit#gid=1469978055',
           sheet = 'Additional NA Twitter Data')

H1 2022 Meta Ask (Top 7 Campaigns)

Data Read-in

Google Sheet with BQ Results

Social_2022_raw_Top7Meta_data = googlesheets4::read_sheet(
  "https://docs.google.com/spreadsheets/d/1RU0QGpnn7FHtZaZfCA22kCcGpEeY1vLh0pHkJ9DEZfU/edit#gid=1752984558") %>% 
  clean_names() %>% 
  filter(impressions_analyzed > 10)
The googlesheets4 package is requesting access to your Google account.
Select a pre-authorised account or enter '0' to obtain a new token.
Press Esc/Ctrl + C to cancel.

1: darshan.patel@essenceglobal.com
1
Auto-refreshing stale OAuth token.
√ Reading from H1 2022 Meta Ask v2.
√ Range Results.
Social_2022_MOAT_lookup = googlesheets4::read_sheet(
  "https://docs.google.com/spreadsheets/d/1nDXokcwtq_J8A6Uh7zAxsWW16MtcEC_1EiLstz4WGig/edit#gid=1782979571") %>% 
  clean_names() %>% 
  filter(social == 'Y')
√ Reading from Benchmark_Moat_Tile_2022.
√ Range Benchmark_Moat_Tile_2022.

Social_2022_processed_Top7Meta_data <-
  Social_2022_raw_Top7Meta_data %>%
  filter(platform == 'Facebook') %>% 
  select(
    olive_plan_id:opid, impressions_analyzed,
    x2_sec_video_in_view_impressions, in_view_impressions, fully_on_screen_3sec_cumulative, player_vis_and_aud_on_complete_sum,
    valid_and_avoc, valid_and_viewable, valid_and_viewable_gm, valid_and_fully_on_screen_3sec_cumulative, valid_and_inview_3sec_cumulative
  ) %>%
  rename(platform_old = platform
         ) %>% 
  left_join(Social_2022_MOAT_lookup, by = 'brand_id'
  ) %>% 
  mutate(
    olive_plan_id = as.character(olive_plan_id),
    olive_placement_id = as.character(olive_placement_id),
    brand_id = as.character(brand_id) ,
    opid = as.character(opid)
  )

#Check Missing Data

Social_2022_processed_Top7Meta_data %>% 
  skim()
-- Data Summary ------------------------
                           Values    
Name                       Piped data
Number of rows             4273      
Number of columns          24        
_______________________              
Column type frequency:               
  character                13        
  numeric                  10        
  POSIXct                  1         
________________________             
Group variables            None      

-- Variable type: character -------------------------------------------------------------------------------------------------------------
# A tibble: 13 x 8
   skim_variable        n_missing complete_rate   min   max empty n_unique whitespace
 * <chr>                    <int>         <dbl> <int> <int> <int>    <int>      <int>
 1 olive_plan_id                0             1     4     5     0        7          0
 2 olive_plan_name              0             1    43    68     0        7          0
 3 platform_old                 0             1     8     8     0        1          0
 4 olive_placement_id           0             1     7     7     0      124          0
 5 olive_placement_type         0             1    12    21     0        3          0
 6 brand_id                     0             1     7     7     0        5          0
 7 opid                         0             1     7     7     0      124          0
 8 dataset_name                 0             1    32    51     0        5          0
 9 region                       0             1     2     2     0        1          0
10 channel                      0             1     5     7     0        2          0
11 social                       0             1     1     1     0        1          0
12 platform                     0             1     8     9     0        2          0
13 media_type                   0             1     5    13     0        3          0

-- Variable type: numeric ---------------------------------------------------------------------------------------------------------------
# A tibble: 10 x 11
   skim_variable                             n_missing complete_rate    mean      sd    p0    p25     p50     p75    p100 hist   
 * <chr>                                         <int>         <dbl>   <dbl>   <dbl> <dbl>  <dbl>   <dbl>   <dbl>   <dbl> <chr>  
 1 impressions_analyzed                              0         1     172452. 310322.    11   5698  39769  190414  2458527 "▇▁▁▁▁"
 2 x2_sec_video_in_view_impressions                473         0.889  14846.  25037.     0    539   3280.  19332.  255752 "▇▁▁▁▁"
 3 in_view_impressions                            3800         0.111 320224. 223119.  5632 137023 269619  443635  1111029 "▇▆▃▁▁"
 4 fully_on_screen_3sec_cumulative                4273         0        NaN      NA     NA     NA     NA      NA       NA " "    
 5 player_vis_and_aud_on_complete_sum              473         0.889   1255.   2535.     0     24    323    1200.   24082 "▇▁▁▁▁"
 6 valid_and_avoc                                  473         0.889   1255.   2535.     0     24    323    1200.   24082 "▇▁▁▁▁"
 7 valid_and_viewable                                0         1      48649. 123451.     0    697   4979   28720  1111029 "▇▁▁▁▁"
 8 valid_and_viewable_gm                          3800         0.111 667387. 469658. 11014 294637 561180  924167  2296318 "▇▆▃▁▁"
 9 valid_and_fully_on_screen_3sec_cumulative      4273         0        NaN      NA     NA     NA     NA      NA       NA " "    
10 valid_and_inview_3sec_cumulative                473         0.889  10966.  17556.     0    410   2431   15043.  198035 "▇▁▁▁▁"

-- Variable type: POSIXct ---------------------------------------------------------------------------------------------------------------
# A tibble: 1 x 7
  skim_variable n_missing complete_rate min                 max                 median              n_unique
* <chr>             <int>         <dbl> <dttm>              <dttm>              <dttm>                 <int>
1 date                  0             1 2020-04-27 00:00:00 2021-06-30 00:00:00 2020-12-09 00:00:00      197

Plot Impression Level Data


p = Social_2022_processed_Top7Meta_data %>% 
  ggplot() +
    aes(x = date, y = impressions_analyzed, color = olive_plan_name, fill = olive_plan_name) +
  xlab("Date") +
  ylab("Impressions") +
  ggtitle("Meta Impressions by Campaign") + #fix size
geom_bar(stat = 'identity') +
  theme_bw() +
  theme(
     plot.title = element_text(size=22, hjust = 0.5),
     axis.title.y = element_blank()
  )

p


ggplotly(p)

Calculate V/V and AVOC Rates by Platform

Social_2022_Summarized_Top7Meta_data <-
Social_2022_processed_Top7Meta_data %>%
  mutate(
    valid_viewable_imps =
      case_when(
        platform == 'Twitter' & channel == 'Display' ~ valid_and_inview_3sec_cumulative,
        platform == 'LinkedIn' ~ x2_sec_video_in_view_impressions,
        TRUE ~ valid_and_viewable
      ),
    avoc_imps = case_when(
    is.na(player_vis_and_aud_on_complete_sum) ~ 0,
    TRUE ~  player_vis_and_aud_on_complete_sum 
    ),
    quarter = lubridate::quarter(date),
    channel = case_when(
      channel == 'DISPLAY' ~ 'STATIC',
      TRUE ~ channel
    )
  ) %>% 
  group_by(olive_plan_name,date, channel, platform) %>% 
  summarize(
    valid_viewable_imps = sum(valid_viewable_imps),
    avoc_imps = sum(avoc_imps),
    impressions = sum(impressions_analyzed),
    quarter = max(quarter)
  ) %>% 
  arrange(date,olive_plan_name)
`summarise()` has grouped output by 'olive_plan_name', 'date', 'channel'. You can override using the `.groups` argument.

Summarized Table - Campaign

Sumif Documentation


Social_2022_Summarized_Top7Meta_data %>% 
  group_by(olive_plan_name) %>% 
  summarize(
    `Valid and Viewable Rate` = sum(valid_viewable_imps)/sum(impressions),
    `Video vs Static` = 1 - sum(impressions[channel =='STATIC'])/sum(impressions)
  ) %>%     
  kbl() %>% 
 kable_material(c("striped", "hover","condensed","responsive"),full_width = F,fixed_thead = T)
olive_plan_name Valid and Viewable Rate Video vs Static
Chromebook NA Q4 2020 United States - Brand 0.14829 1.0000
Google FI Q2 2021 United States - Brand Demand Gen 0.11471 1.0000
Grow with Google NA Q2 2021 United States - Career Certifications 0.19344 1.0000
Pixel US Ramble Q1 2021 North America - Brand 0.41812 0.0666
Pixel US Ramble Q2 2021 North America - Brand 0.46276 0.0000
YouTube NA 2020 Q4 2020 United States - Longtail_Brand Voice_#669762 0.12791 1.0000
YouTube NA Q2 2020 United States - Longtail_James Charles_#669762 0.11704 1.0000

Write to Google Sheets


Social_2022_Summarized_Top7Meta_data_Table <-
Social_2022_Summarized_Top7Meta_data %>% 
  group_by(olive_plan_name) %>% 
  summarize(
    `Valid and Viewable Rate` = sum(valid_viewable_imps)/sum(impressions),
    `Video vs Static` = 1 - sum(impressions[channel =='STATIC'])/sum(impressions)
  )
write_sheet(Social_2022_Summarized_Top7Meta_data_Table, ss = 'https://docs.google.com/spreadsheets/d/1RU0QGpnn7FHtZaZfCA22kCcGpEeY1vLh0pHkJ9DEZfU/edit#gid=1752984558',
           sheet = 'Aggregated Data From R')
√ Writing to H1 2022 Meta Ask v2.
√ Writing to sheet Aggregated Data From R.
LS0tDQp0aXRsZTogIjAxX0RhdGFfUmVhZGluX1N1bW1hcnlfMjAyMk5BX1NvY2lhbCINCmF1dGhvcjogIkRhcnNoYW4gUGF0ZWwiDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4NCiAgICBmaWdfd2lkdGg6IDcNCiAgICBmaWdfaGVpZ2h0OiA2DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KI25vdGU6IG5vcm1hbGx5IGluY2x1ZGUgPSBGQUxTRSBmb3IgdGhpcw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKSAjYWxsIGNvZGUgY2h1bmtzIGJ5IGRlZmF1bHQgd2lsbCBiZSBzaG93bg0Kb3B0aW9ucyhrbml0ci50YWJsZS5mb3JtYXQgPSAiaHRtbCIpICN0YWJsZSBmb3JtYXQNCm9wdGlvbnMoZGlnaXRzPTUpICNzZXQgZGlnaXRzIGluIG51bWJlcnMNCm9wdGlvbnMoc2NpcGVuID0gMTAwKSAjZGlnaXRzIHNob3cgYmVmb3JlIHVzaW5nIHNjaWVudGlmaWMgbm90YXRpb24NCmtuaXRyOjpvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHM9bGlzdCh3aWR0aC5jdXRvZmY9ODApLCB0aWR5PVRSVUUpDQojaW5zdGFsbC5wYWNrYWdlcygicGFjbWFuIikNCmxpYnJhcnkocGFjbWFuKSAjZm9yIHF1aWNrIGxvYWQvaW5zdGFsbCBvZiBwYWNrYWdlcw0KcF9sb2FkKA0KICBkcGx5ciwgcmVhZHIsIHRpZHl2ZXJzZSxmb3JjYXRzLHB1cnJyLGx1YnJpZGF0ZSwgIyByZWFkaW5nIGluIGRhdGENCiAgamFuaXRvciwgc3FsZGYsZ29vZ2xlc2hlZXRzNCwgIyBhZGRpdGlvbmFsIHRvb2xzIGZvciBkZWFsaW5nIHdpdGggZGF0YQ0KICBza2ltciwNCiAgcnFkYXRhdGFibGUsICMNCiAgc3BsaXRzdGFja3NoYXBlLHN0cmluZ3IsICNzdHJpbmcgcmVsYXRlZCBsaWJyYXJpZXMNCiAga2FibGVFeHRyYSwgZ2dwbG90MiwgcGxvdGx5LGVjaGFydHM0cixnZ3B1YnIsc2NhbGVzLFJDb2xvckJyZXdlcixnZ3RoZW1lcywgI2ZvciB2aXN1YWxpemF0aW9uIG9mIGRhdGENCiAgcmV0aWN1bGF0ZSAjZm9yIHVzaW5nIHB5dGhvbg0KKQ0KYGBgDQoNCg0KIyMgRGF0YSBSZWFkLWluDQoNCmh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFuRFhva2N3dHFfSjhBNlVoN3pBeHNXVzE2TXRjRUNfMUVpTHN0ejRXR2lnL2VkaXQjZ2lkPTE3ODI5Nzk1NzENCg0KYGBge3J9DQpTb2NpYWxfMjAyMl9yYXdOQV9kYXRhIDwtIHJlYWRyOjpyZWFkX2NzdigiREFUQS9Tb2NpYWwgUXVlcnkgMjAyMiBOQSBDYW1wYWlnbnMuY3N2IikgJT4lDQogIGNsZWFuX25hbWVzKCkgJT4lIA0KICBmaWx0ZXIoZGF0ZSA8ICcyMDIyLTA3LTAxJykgJT4lIA0KICBmaWx0ZXIoaW1wcmVzc2lvbnNfYW5hbHl6ZWQgPiAxMCkNCg0KDQpTb2NpYWxfMjAyMl9yYXdOQV9sb29rdXAgPC0gcmVhZHI6OnJlYWRfY3N2KCJEQVRBL0NhbXBhaWduTG9va3VwLmNzdiIpICU+JQ0KICBjbGVhbl9uYW1lcygpDQoNClNvY2lhbF8yMDIyX01PQVRfbG9va3VwID0gZ29vZ2xlc2hlZXRzNDo6cmVhZF9zaGVldCgNCiAgImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFuRFhva2N3dHFfSjhBNlVoN3pBeHNXVzE2TXRjRUNfMUVpTHN0ejRXR2lnL2VkaXQjZ2lkPTE3ODI5Nzk1NzEiKSAlPiUgDQogIGNsZWFuX25hbWVzKCkgJT4lIA0KICBmaWx0ZXIoc29jaWFsID09ICdZJykNCg0KYGBgDQoNCiMjIFN1bW1hcnkgU3RhdHMNCg0KIyMjIFBsYWNlbWVudCB0eXBlcyBkaXN0aW5jdA0KYGBge3J9DQoNClNvY2lhbF8yMDIyX3Jhd05BX2RhdGEgJT4lDQogIGdyb3VwX2J5KHBsYXRmb3JtLG9saXZlX3BsYWNlbWVudF90eXBlKSAlPiUgDQogIHN1bW1hcml6ZSAoY291bnRzID0gbigpKQ0KDQpTb2NpYWxfMjAyMl9yYXdOQV9kYXRhICU+JQ0KICBncm91cF9ieShwbGF0Zm9ybSxicmFuZF9pZCkgJT4lIA0KICBzdW1tYXJpemUgKGNvdW50cyA9IG4oKSkgJT4lDQogIGxlZnRfam9pbihTb2NpYWxfMjAyMl9NT0FUX2xvb2t1cCwgYnkgPSAnYnJhbmRfaWQnKQ0KDQpgYGANCg0KYGBge3J9DQoNClNvY2lhbF8yMDIyX3Byb2Nlc3NlZE5BX2RhdGEgPC0NCiAgU29jaWFsXzIwMjJfcmF3TkFfZGF0YSAlPiUNCiAgc2VsZWN0KA0KICAgIG9saXZlX3BsYW5faWQ6b3BpZCwgaW1wcmVzc2lvbnNfYW5hbHl6ZWQsDQogICAgeDJfc2VjX3ZpZGVvX2luX3ZpZXdfaW1wcmVzc2lvbnMsIGluX3ZpZXdfaW1wcmVzc2lvbnMsIGZ1bGx5X29uX3NjcmVlbl8zc2VjX2N1bXVsYXRpdmUsIHBsYXllcl92aXNfYW5kX2F1ZF9vbl9jb21wbGV0ZV9zdW0sDQogICAgdmFsaWRfYW5kX2F2b2MsIHZhbGlkX2FuZF92aWV3YWJsZSwgdmFsaWRfYW5kX3ZpZXdhYmxlX2dtLCB2YWxpZF9hbmRfZnVsbHlfb25fc2NyZWVuXzNzZWNfY3VtdWxhdGl2ZSwgdmFsaWRfYW5kX2ludmlld18zc2VjX2N1bXVsYXRpdmUNCiAgKSAlPiUNCiAgcmVuYW1lKHBsYXRmb3JtX29sZCA9IHBsYXRmb3JtDQogICAgICAgICApICU+JSANCiAgbGVmdF9qb2luKFNvY2lhbF8yMDIyX01PQVRfbG9va3VwLCBieSA9ICdicmFuZF9pZCcNCiAgKSAlPiUgDQogIG11dGF0ZSgNCiAgICBvbGl2ZV9wbGFuX2lkID0gYXMuY2hhcmFjdGVyKG9saXZlX3BsYW5faWQpLA0KICAgIG9saXZlX3BsYWNlbWVudF9pZCA9IGFzLmNoYXJhY3RlcihvbGl2ZV9wbGFjZW1lbnRfaWQpLA0KICAgIGJyYW5kX2lkID0gYXMuY2hhcmFjdGVyKGJyYW5kX2lkKSAsDQogICAgb3BpZCA9IGFzLmNoYXJhY3RlcihvcGlkKQ0KICApDQoNCiNMb29rIGF0IGdsb2JhbCBtb2F0DQoNClNvY2lhbF8yMDIyX3Byb2Nlc3NlZE5BX2RhdGEgJT4lIA0KICBza2ltKCkNCmBgYA0KDQojIyMgUGxvdCBJbXByZXNzaW9uIExldmVsIERhdGENCmBgYHtyLCBmaWcuaGVpZ2h0PSAxMCwgZmlnLndpZHRoPSAxNX0NCg0KcCA9IFNvY2lhbF8yMDIyX3Byb2Nlc3NlZE5BX2RhdGEgJT4lIA0KICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSBkYXRlLCB5ID0gaW1wcmVzc2lvbnNfYW5hbHl6ZWQsIGNvbG9yID0gb2xpdmVfcGxhbl9uYW1lLCBmaWxsID0gb2xpdmVfcGxhbl9uYW1lKSArDQogIHhsYWIoIkRhdGUiKSArDQogIHlsYWIoIkltcHJlc3Npb25zIikgKw0KICBnZ3RpdGxlKCJJbXByZXNzaW9ucyBieSBDYW1wYWlnbiIpICsgI2ZpeCBzaXplDQpnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoDQogICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMiwgaGp1c3QgPSAwLjUpLA0KICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KDQpwDQoNCmdncGxvdGx5KHApDQpgYGANCg0KIyMjIENhbGN1bGF0ZSBWL1YgYW5kIEFWT0MgUmF0ZXMgYnkgUGxhdGZvcm0NCmBgYHtyfQ0KU29jaWFsXzIwMjJfU3VtbWFyaXplZE5BX2RhdGUgPC0NClNvY2lhbF8yMDIyX3Byb2Nlc3NlZE5BX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICB2YWxpZF92aWV3YWJsZV9pbXBzID0NCiAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgcGxhdGZvcm0gPT0gJ1R3aXR0ZXInICYgY2hhbm5lbCA9PSAnRGlzcGxheScgfiB2YWxpZF9hbmRfaW52aWV3XzNzZWNfY3VtdWxhdGl2ZSwNCiAgICAgICAgcGxhdGZvcm0gPT0gJ0xpbmtlZEluJyB+IHgyX3NlY192aWRlb19pbl92aWV3X2ltcHJlc3Npb25zLA0KICAgICAgICBUUlVFIH4gdmFsaWRfYW5kX3ZpZXdhYmxlDQogICAgICApLA0KICAgIGF2b2NfaW1wcyA9IGNhc2Vfd2hlbigNCiAgICBUUlVFIH4gIHBsYXllcl92aXNfYW5kX2F1ZF9vbl9jb21wbGV0ZV9zdW0gDQogICAgKSwNCiAgICBxdWFydGVyID0gbHVicmlkYXRlOjpxdWFydGVyKGRhdGUpLA0KICAgIGNoYW5uZWwgPSBjYXNlX3doZW4oDQogICAgICBjaGFubmVsID09ICdESVNQTEFZJyB+ICdTVEFUSUMnLA0KICAgICAgVFJVRSB+IGNoYW5uZWwNCiAgICApDQogICkgJT4lIA0KICBncm91cF9ieShvbGl2ZV9wbGFuX25hbWUsZGF0ZSwgY2hhbm5lbCwgcGxhdGZvcm0pICU+JSANCiAgc3VtbWFyaXplKA0KICAgIHZhbGlkX3ZpZXdhYmxlX2ltcHMgPSBzdW0odmFsaWRfdmlld2FibGVfaW1wcyksDQogICAgYXZvY19pbXBzID0gc3VtKGF2b2NfaW1wcyksDQogICAgaW1wcmVzc2lvbnMgPSBzdW0oaW1wcmVzc2lvbnNfYW5hbHl6ZWQpLA0KICAgIHF1YXJ0ZXIgPSBtYXgocXVhcnRlcikNCiAgKSAlPiUgDQogIGFycmFuZ2UoZGF0ZSxvbGl2ZV9wbGFuX25hbWUpDQpgYGANCg0KYGBge3J9DQoNClNvY2lhbF8yMDIyX1N1bW1hcml6ZWROQV9kYXRlICU+JSANCiAgZmlsdGVyKHBsYXRmb3JtID09ICdUd2l0dGVyJykgJT4lIA0KICBmaWx0ZXIoY2hhbm5lbCA9PSAnRElTUExBWScpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQoNClNvY2lhbF8yMDIyX1N1bW1hcml6ZWROQV9kYXRlICU+JSANCiAgZ3JvdXBfYnkocXVhcnRlciwgcGxhdGZvcm0sIGNoYW5uZWwpICU+JSANCiAgc3VtbWFyaXplKA0KICAgIGBWYWxpZCBhbmQgVmlld2FibGUgUmF0ZWAgPSBzdW0odmFsaWRfdmlld2FibGVfaW1wcykvc3VtKGltcHJlc3Npb25zKSwNCiAgICBgQVZPQyBSYXRlYCA9IHN1bShhdm9jX2ltcHMpL3N1bShpbXByZXNzaW9ucykNCiAgKSAlPiUgICAgIA0KICBrYmwoKSAlPiUgDQoga2FibGVfbWF0ZXJpYWwoYygic3RyaXBlZCIsICJob3ZlciIsImNvbmRlbnNlZCIsInJlc3BvbnNpdmUiKSxmdWxsX3dpZHRoID0gRixmaXhlZF90aGVhZCA9IFQpDQpgYGANCg0KYGBge3J9DQp3cml0ZV9zaGVldChTb2NpYWxfMjAyMl9TdW1tYXJpemVkTkFfZGF0ZSwgc3MgPSAnaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMU9UN3pvcWJjbmFkUXNSOGVoSGJ2TWZLbjhtaWVXWWEtZFZhQ09IRmRoSEkvZWRpdCNnaWQ9MTQ2OTk3ODA1NScsDQogICAgICAgICAgIHNoZWV0ID0gJ0FnZ3JlZ2F0ZWQgRGF0YSBOQScpDQpgYGANCg0KIyMjIEFkZGl0aW9uYWwgVHdpdHRlciBMZXZlbCBJbmZvcm1hdGlvbg0KDQpgYGB7cn0NCg0KU29jaWFsXzIwMjJfcmF3TkFfRFYzMF9kYXRhIDwtIHJlYWRyOjpyZWFkX2NzdigiREFUQS9Tb2NpYWwgUXVlcnkgMjAyMiBOQSBEVjM2MC5jc3YiKSAlPiUNCiAgY2xlYW5fbmFtZXMoKSAlPiUgDQogIGZpbHRlcihkYXRlIDwgJzIwMjItMDctMDEnKSAlPiUgDQogIG11dGF0ZSgNCiAgICBvbGl2ZV9wbGFjZW1lbnRfaWQgPSBhcy5jaGFyYWN0ZXIob2xpdmVfcGxhY2VtZW50X2lkKQ0KICApDQogIA0KDQpTb2NpYWxfMjAyMl9wcm9jZXNzZWRUd2l0dGVyTkFfZGF0YSA8LQ0KU29jaWFsXzIwMjJfcHJvY2Vzc2VkTkFfZGF0YSAlPiUgc2VsZWN0KG9saXZlX3BsYW5fbmFtZSxvbGl2ZV9wbGFjZW1lbnRfaWQsZGF0ZSxpbXByZXNzaW9uc19hbmFseXplZCkgJT4lDQogIG11dGF0ZSgNCiAgICBxdWFydGVyID0gcXVhcnRlcihkYXRlKQ0KICApICU+JSANCiAgZ3JvdXBfYnkob2xpdmVfcGxhbl9uYW1lLG9saXZlX3BsYWNlbWVudF9pZCxkYXRlLHF1YXJ0ZXIpICU+JSANCiAgc3VtbWFyaXplKA0KICAgIGltcHJlc3Npb25zX2FuYWx5emVkID0gc3VtKGltcHJlc3Npb25zX2FuYWx5emVkKQ0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lIA0KICBsZWZ0X2pvaW4oU29jaWFsXzIwMjJfcmF3TkFfRFYzMF9kYXRhLCBieSA9IGMoJ29saXZlX3BsYWNlbWVudF9pZCcsJ29saXZlX3BsYW5fbmFtZScsICdkYXRlJykpICU+JSANCiAgZmlsdGVyKCFpcy5uYShvbGl2ZV9wbGFuX25hbWUpKSAlPiUgDQogIGZpbHRlcihwbGF0Zm9ybSA9PSAnVHdpdHRlciAtIE9mZmljaWFsJyAmIG9saXZlX3BsYWNlbWVudF90eXBlID09ICdTdGFuZGFyZCBCYW5uZXIgKEJBTiknKSAlPiUgDQogIG11dGF0ZSgNCiAgICBpbXByZXNzaW9ucyA9IGltcHJlc3Npb25zX2FuYWx5emVkLA0KICAgIHBsYXRmb3JtID0gJ1R3aXR0ZXInLA0KICAgIGNoYW5uZWwgPSAnU1RBVElDJywNCiAgICBhdm9jX2ltcHMgPSBOQSwNCiAgICB2YWxpZF92aWV3YWJsZV9pbXBzID0gdmFsaWRfYW5kX3ZpZXdhYmxlX2ltcHJlc3Npb25zLA0KICAgIFRpbWUgPSBOQQ0KICApICU+JSANCiAgc2VsZWN0KG9saXZlX3BsYW5fbmFtZSwJZGF0ZSwJY2hhbm5lbCwJcGxhdGZvcm0sCXZhbGlkX3ZpZXdhYmxlX2ltcHMsCWF2b2NfaW1wcywJaW1wcmVzc2lvbnMsCVRpbWUsCXF1YXJ0ZXIpDQoNCmBgYA0KDQpgYGB7cn0NCg0KU29jaWFsXzIwMjJfU3VtbWFyaXplZE5BX2RhdGFfdXBkYXRlZFR3aXR0ZXIgPC0NClNvY2lhbF8yMDIyX1N1bW1hcml6ZWROQV9kYXRlICU+JQ0KICBmaWx0ZXIoIShwbGF0Zm9ybSA9PSAnVHdpdHRlcicgJiBjaGFubmVsID09ICdTVEFUSUMnKSkgJT4lICAgDQogIGJpbmRfcm93cyAoU29jaWFsXzIwMjJfcHJvY2Vzc2VkVHdpdHRlck5BX2RhdGEpDQoNCmBgYA0KDQojIyMgVXBkYXRlIFR3aXR0ZXIgSW5mb3JtYXRpb24NCg0KYGBge3J9DQoNCg0Kd3JpdGVfc2hlZXQoU29jaWFsXzIwMjJfU3VtbWFyaXplZE5BX2RhdGFfdXBkYXRlZFR3aXR0ZXIsIHNzID0gJ2h0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFPVDd6b3FiY25hZFFzUjhlaEhidk1mS244bWllV1lhLWRWYUNPSEZkaEhJL2VkaXQjZ2lkPTE0Njk5NzgwNTUnLA0KICAgICAgICAgICBzaGVldCA9ICdBZGRpdGlvbmFsIE5BIFR3aXR0ZXIgRGF0YScpDQoNCmBgYA0KICANCioqKiAgIA0KDQojIEgxIDIwMjIgTWV0YSBBc2sgKFRvcCA3IENhbXBhaWducykgIA0KDQojIyBEYXRhIFJlYWQtaW4NCg0KW0dvb2dsZSBTaGVldCB3aXRoIEJRIFJlc3VsdHNdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFSVTBRR3BubjdGSHRaYVpmQ0EyMmtDY0dwRWVZMXZMaDBwSGtKOURFWmZVL2VkaXQjZ2lkPTE3NTI5ODQ1NTgpICANCg0KYGBge3J9DQpTb2NpYWxfMjAyMl9yYXdfVG9wN01ldGFfZGF0YSA9IGdvb2dsZXNoZWV0czQ6OnJlYWRfc2hlZXQoDQogICJodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xUlUwUUdwbm43Rkh0WmFaZkNBMjJrQ2NHcEVlWTF2TGgwcEhrSjlERVpmVS9lZGl0I2dpZD0xNzUyOTg0NTU4IikgJT4lIA0KICBjbGVhbl9uYW1lcygpICU+JSANCiAgZmlsdGVyKGltcHJlc3Npb25zX2FuYWx5emVkID4gMTApDQoNCg0KU29jaWFsXzIwMjJfTU9BVF9sb29rdXAgPSBnb29nbGVzaGVldHM0OjpyZWFkX3NoZWV0KA0KICAiaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMW5EWG9rY3d0cV9KOEE2VWg3ekF4c1dXMTZNdGNFQ18xRWlMc3R6NFdHaWcvZWRpdCNnaWQ9MTc4Mjk3OTU3MSIpICU+JSANCiAgY2xlYW5fbmFtZXMoKSAlPiUgDQogIGZpbHRlcihzb2NpYWwgPT0gJ1knKQ0KYGBgDQoNCmBgYHtyfQ0KDQpTb2NpYWxfMjAyMl9yYXdfVG9wN01ldGFfZGF0YSAlPiUgDQogIGdyb3VwX2J5KHBsYXRmb3JtKSAlPiUgDQogIHN1bW1hcml6ZShkaXN0ID0gbigpKQ0KYGBgDQoNCg0KYGBge3J9DQoNClNvY2lhbF8yMDIyX3Byb2Nlc3NlZF9Ub3A3TWV0YV9kYXRhIDwtDQogIFNvY2lhbF8yMDIyX3Jhd19Ub3A3TWV0YV9kYXRhICU+JQ0KICBmaWx0ZXIocGxhdGZvcm0gPT0gJ0ZhY2Vib29rJykgJT4lIA0KICBzZWxlY3QoDQogICAgb2xpdmVfcGxhbl9pZDpvcGlkLCBpbXByZXNzaW9uc19hbmFseXplZCwNCiAgICB4Ml9zZWNfdmlkZW9faW5fdmlld19pbXByZXNzaW9ucywgaW5fdmlld19pbXByZXNzaW9ucywgZnVsbHlfb25fc2NyZWVuXzNzZWNfY3VtdWxhdGl2ZSwgcGxheWVyX3Zpc19hbmRfYXVkX29uX2NvbXBsZXRlX3N1bSwNCiAgICB2YWxpZF9hbmRfYXZvYywgdmFsaWRfYW5kX3ZpZXdhYmxlLCB2YWxpZF9hbmRfdmlld2FibGVfZ20sIHZhbGlkX2FuZF9mdWxseV9vbl9zY3JlZW5fM3NlY19jdW11bGF0aXZlLCB2YWxpZF9hbmRfaW52aWV3XzNzZWNfY3VtdWxhdGl2ZQ0KICApICU+JQ0KICByZW5hbWUocGxhdGZvcm1fb2xkID0gcGxhdGZvcm0NCiAgICAgICAgICkgJT4lIA0KICBsZWZ0X2pvaW4oU29jaWFsXzIwMjJfTU9BVF9sb29rdXAsIGJ5ID0gJ2JyYW5kX2lkJw0KICApICU+JSANCiAgbXV0YXRlKA0KICAgIG9saXZlX3BsYW5faWQgPSBhcy5jaGFyYWN0ZXIob2xpdmVfcGxhbl9pZCksDQogICAgb2xpdmVfcGxhY2VtZW50X2lkID0gYXMuY2hhcmFjdGVyKG9saXZlX3BsYWNlbWVudF9pZCksDQogICAgYnJhbmRfaWQgPSBhcy5jaGFyYWN0ZXIoYnJhbmRfaWQpICwNCiAgICBvcGlkID0gYXMuY2hhcmFjdGVyKG9waWQpDQogICkNCg0KI0NoZWNrIE1pc3NpbmcgRGF0YQ0KDQpTb2NpYWxfMjAyMl9wcm9jZXNzZWRfVG9wN01ldGFfZGF0YSAlPiUgDQogIHNraW0oKQ0KDQpgYGANCg0KDQojIyMgUGxvdCBJbXByZXNzaW9uIExldmVsIERhdGENCmBgYHtyLCBmaWcuaGVpZ2h0PSAxMCwgZmlnLndpZHRoPSAyMH0NCg0KcCA9IFNvY2lhbF8yMDIyX3Byb2Nlc3NlZF9Ub3A3TWV0YV9kYXRhICU+JSANCiAgZ2dwbG90KCkgKw0KICAgIGFlcyh4ID0gZGF0ZSwgeSA9IGltcHJlc3Npb25zX2FuYWx5emVkLCBjb2xvciA9IG9saXZlX3BsYW5fbmFtZSwgZmlsbCA9IG9saXZlX3BsYW5fbmFtZSkgKw0KICB4bGFiKCJEYXRlIikgKw0KICB5bGFiKCJJbXByZXNzaW9ucyIpICsNCiAgZ2d0aXRsZSgiTWV0YSBJbXByZXNzaW9ucyBieSBDYW1wYWlnbiIpICsgI2ZpeCBzaXplDQpnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoDQogICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMiwgaGp1c3QgPSAwLjUpLA0KICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KDQpwDQoNCmdncGxvdGx5KHApDQpgYGANCg0KIyMjIENhbGN1bGF0ZSBWL1YgYW5kIEFWT0MgUmF0ZXMgYnkgUGxhdGZvcm0NCmBgYHtyfQ0KU29jaWFsXzIwMjJfU3VtbWFyaXplZF9Ub3A3TWV0YV9kYXRhIDwtDQpTb2NpYWxfMjAyMl9wcm9jZXNzZWRfVG9wN01ldGFfZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIHZhbGlkX3ZpZXdhYmxlX2ltcHMgPQ0KICAgICAgY2FzZV93aGVuKA0KICAgICAgICBwbGF0Zm9ybSA9PSAnVHdpdHRlcicgJiBjaGFubmVsID09ICdEaXNwbGF5JyB+IHZhbGlkX2FuZF9pbnZpZXdfM3NlY19jdW11bGF0aXZlLA0KICAgICAgICBwbGF0Zm9ybSA9PSAnTGlua2VkSW4nIH4geDJfc2VjX3ZpZGVvX2luX3ZpZXdfaW1wcmVzc2lvbnMsDQogICAgICAgIFRSVUUgfiB2YWxpZF9hbmRfdmlld2FibGUNCiAgICAgICksDQogICAgYXZvY19pbXBzID0gY2FzZV93aGVuKA0KICAgIGlzLm5hKHBsYXllcl92aXNfYW5kX2F1ZF9vbl9jb21wbGV0ZV9zdW0pIH4gMCwNCiAgICBUUlVFIH4gIHBsYXllcl92aXNfYW5kX2F1ZF9vbl9jb21wbGV0ZV9zdW0gDQogICAgKSwNCiAgICBxdWFydGVyID0gbHVicmlkYXRlOjpxdWFydGVyKGRhdGUpLA0KICAgIGNoYW5uZWwgPSBjYXNlX3doZW4oDQogICAgICBjaGFubmVsID09ICdESVNQTEFZJyB+ICdTVEFUSUMnLA0KICAgICAgVFJVRSB+IGNoYW5uZWwNCiAgICApDQogICkgJT4lIA0KICBncm91cF9ieShvbGl2ZV9wbGFuX25hbWUsZGF0ZSwgY2hhbm5lbCwgcGxhdGZvcm0pICU+JSANCiAgc3VtbWFyaXplKA0KICAgIHZhbGlkX3ZpZXdhYmxlX2ltcHMgPSBzdW0odmFsaWRfdmlld2FibGVfaW1wcyksDQogICAgYXZvY19pbXBzID0gc3VtKGF2b2NfaW1wcyksDQogICAgaW1wcmVzc2lvbnMgPSBzdW0oaW1wcmVzc2lvbnNfYW5hbHl6ZWQpLA0KICAgIHF1YXJ0ZXIgPSBtYXgocXVhcnRlcikNCiAgKSAlPiUgDQogIGFycmFuZ2UoZGF0ZSxvbGl2ZV9wbGFuX25hbWUpDQpgYGANCg0KDQojIyMgU3VtbWFyaXplZCBUYWJsZSAtIENhbXBhaWduDQpbU3VtaWYgRG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjM1Mjg4NjIvc3VtbWFyaXplLWFsbC1ncm91cC12YWx1ZXMtYW5kLWEtY29uZGl0aW9uYWwtc3Vic2V0LWluLXRoZS1zYW1lLWNhbGwpDQoNCmBgYHtyfQ0KDQpTb2NpYWxfMjAyMl9TdW1tYXJpemVkX1RvcDdNZXRhX2RhdGEgJT4lIA0KICBncm91cF9ieShvbGl2ZV9wbGFuX25hbWUpICU+JSANCiAgc3VtbWFyaXplKA0KICAgIGBWYWxpZCBhbmQgVmlld2FibGUgUmF0ZWAgPSBzdW0odmFsaWRfdmlld2FibGVfaW1wcykvc3VtKGltcHJlc3Npb25zKSwNCiAgICBgVmlkZW8gdnMgU3RhdGljYCA9IDEgLSBzdW0oaW1wcmVzc2lvbnNbY2hhbm5lbCA9PSdTVEFUSUMnXSkvc3VtKGltcHJlc3Npb25zKQ0KICApICU+JSAgICAgDQogIGtibCgpICU+JSANCiBrYWJsZV9tYXRlcmlhbChjKCJzdHJpcGVkIiwgImhvdmVyIiwiY29uZGVuc2VkIiwicmVzcG9uc2l2ZSIpLGZ1bGxfd2lkdGggPSBGLGZpeGVkX3RoZWFkID0gVCkNCmBgYA0KDQojIyMgV3JpdGUgdG8gR29vZ2xlIFNoZWV0cw0KDQpgYGB7cn0NCg0KU29jaWFsXzIwMjJfU3VtbWFyaXplZF9Ub3A3TWV0YV9kYXRhX1RhYmxlIDwtDQpTb2NpYWxfMjAyMl9TdW1tYXJpemVkX1RvcDdNZXRhX2RhdGEgJT4lIA0KICBncm91cF9ieShvbGl2ZV9wbGFuX25hbWUpICU+JSANCiAgc3VtbWFyaXplKA0KICAgIGBWYWxpZCBhbmQgVmlld2FibGUgUmF0ZWAgPSBzdW0odmFsaWRfdmlld2FibGVfaW1wcykvc3VtKGltcHJlc3Npb25zKSwNCiAgICBgVmlkZW8gdnMgU3RhdGljYCA9IDEgLSBzdW0oaW1wcmVzc2lvbnNbY2hhbm5lbCA9PSdTVEFUSUMnXSkvc3VtKGltcHJlc3Npb25zKQ0KICApDQoNCmBgYA0KDQoNCg0KYGBge3J9DQp3cml0ZV9zaGVldChTb2NpYWxfMjAyMl9TdW1tYXJpemVkX1RvcDdNZXRhX2RhdGFfVGFibGUsIHNzID0gJ2h0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFSVTBRR3BubjdGSHRaYVpmQ0EyMmtDY0dwRWVZMXZMaDBwSGtKOURFWmZVL2VkaXQjZ2lkPTE3NTI5ODQ1NTgnLA0KICAgICAgICAgICBzaGVldCA9ICdBZ2dyZWdhdGVkIERhdGEgRnJvbSBSJykNCmBgYA0KDQoNCg==